#!/usr/bin/python
#-*-coding: utf-8 -*-

__all__ = [u'Parameters']

u"""
Parameters object for pyLot
  - 01-2009, Pierre Puiseux,
  - 05-2009, Pierre Puiseux, Guillaume Baty
Université de Pau et des Pays de l'Adour

.. seealso ::
  UiParameters
"""

import sys, os
from pylot.core import (Book, Datum, Collection, Path, FilePath, check_type)

##########################################################################
class Parameters(Collection):
  u"""
  - Un Parameters est une L{Collection} de :class:`Book`s, chaque :class:`Book` est un bloc.
  - Un :class:`Book` : un dictionnaire de L{Datum}s
  - Un L{Datum} = une variable avec nom, nature, value, default_value, value_list, tooltip, alias, group..
  - Les variables sont donc les Datums, la valeur actuelle est dans Datum.value.
  - La liste des valeurs possibles est dans value_list

  L'ensemble des valeurs contenues dans les I{value_list} des Datum est appelé database

  L'ensemble des valeurs du champ I{value} est appelé paramètres
  """
  def __init__(self, *t, **d):
    u"""
    >>> bloc1 = Book(Datum(u'var1',float,0.07,0.07,[0.8, 12]), Datum(u'var2', int, 1, 1, [3,4,5]), title=u'Bloc1')
    >>> bloc2 = Book(Datum(u'var1',unicode,u'hello',u'hola',[u'bonjour', u'buongiorno']), Datum(u'var2', bool, True, False, [True, False]), title=u'Bloc2')
    >>> p = Parameters(bloc1, bloc2)
    >>> p.name = (u'MaCollec')
    >>> p
    <BLANKLINE>
    //Block Bloc1
      var1 0.07 [0.070000000000000007, 0.80000000000000004, 12.0]
      var2 1 [1, 3, 4, 5]
    //Block Bloc2
      var1 hello [u'bonjour', u'buongiorno', u'hello', u'hola']
      var2 True [False, True]
    >>> p(u'Bloc1',u'var1')
    u'0.07'
    >>> p.addBook(Book(title=u'autre'))
    >>> p._addBlock(u'Nom', Book(title=u'Nom'))
    >>> keys = p.keys() ; keys.sort() ; keys
    [u'Bloc1', u'Bloc2', u'Nom', u'autre']
    >>> keys = p[u'Bloc1'].keys() ; keys.sort() ; keys
    [u'var1', u'var2']

    Soit Param=[parameters_file, database_file, parameters_format, database_format]
    La liste des arguments est une liste de Book, suivie des paramètres de type
    str, donnés par la liste Param, nommés ou ordonnés. Par exemple :

    >>> Parameters(Book(), Book(), u'fileP', u'fileD',u'csv', u'pylot')
    <BLANKLINE>
    //Block None
    """
    self._parameters_file = Datum(u'parameters_file', FilePath)
    self.parameters_format = u'pylot'
    self._database_file = Datum(u'database_file', FilePath)
    self.database_format = u'csv'
    books = []
    lt = []
    if len (t) == 0 :#tous les arg sont nommés
      for key, item in d.items() :
        setattr(self, key, item)
    else :#arguments non nommés : dans l'ordre books, parameters_file, database_file, parameters_format, database_format
      lt = list(t)
      for elem in t :
        if isinstance(elem, Book):
          books.append(lt.pop(0))
        else:#fini les books, on passe aux fichiers
          break
    #books contient les books
    #lt contient des strings
    super(Parameters, self).__init__(*tuple(books))#on appelle le constructeur de Collection
    #et maintenant, les lectures
    for i, attr in enumerate([u'parameters_file', u'database_file', u'parameters_format', u'database_format']) :
      try :
        setattr(self, attr, lt[i])
      except IndexError :
        break

#  def infos(self):
#    info = []
#    for attr in [u'parameters_file', u'database_file', u'parameters_format', u'database_format']:
#      try :info.append('%s : %s' % (attr, getattr(self, attr)))
#      except AttributeError : continue
#    return info


  def _getparameters_file(self):
    return self._parameters_file.value

  def _setparameters_file(self, value):
    self._parameters_file.value = value

  def _getdatabase_file(self):
    return self._database_file.value

  def _setdatabase_file(self, value):
    self._database_file.value = value

  parameters_file = property(fget=_getparameters_file, fset=_setparameters_file)
  database_file = property(fget=_getdatabase_file, fset=_setdatabase_file)

  def infos(self):
    info = {}
    for attr in [u'parameters_file', u'database_file', u'parameters_format', u'database_format']:
      try :info[attr] = getattr(self, attr)
      except AttributeError : continue
    return info

  def _addBlock(self, bloc, book=None) :
    u"""
    Ajoute un bloc de variables, c'est à dire un :class:`Book`.
    :param bloc: nom du bloc. On doit nécessairement avoir bloc == titre du Book.
    :param book: le :class:`Book` à rajouter. Le titre du :class:`Book` est modifié en book.name==bloc
    """
    if book is None :
      book = Book()
    book.name = bloc
    self.addBook(book)




  def newParameter(self, bloc, name, nature, value=None, tooltip=None):
    """
    Creates and returns a new parameter (dat), does NOT add it anywhere
    - If a block (==Book) named bloc exists, create and returns new parameter (dat). 
    Adds it to block bloc
    - If a parameter with the same name, in the block bloc yet exists, 
    return None, and does not add anything
    - If no block bloc exists, return None
    """
    if not self.has_key(bloc) :
      return None
    if self.hasDatum(bloc, name) :
      return None
    val = Datum.convertValueFrompyLot(value, nature)
    dat = Datum(name=name, nature=nature, value=val, tooltip=tooltip)
    self._addParameter(bloc, dat)
    return dat

  def _addParameter(self, bloc, param):
    u"""
    :param bloc: block name
    :type bloc: str
    :param param: parameter to add
    :type param: Datum
    """
    if not self.has_key(bloc):
      self._addBlock(bloc)
    if not self[bloc].has_key(param.name):
      self[bloc].add(param)

  def hasDatum(self, book_id, datum_id):
    if book_id not in self :
      return False
    else :
      return datum_id in self[book_id]

  def collectionItem(self, v):
    u"""
    :param v: a list, [block_name, param_name,...]
    :return: (Book,Datum) Block named in v[0], Datum named in v[1], if any.
    """
    #c = self._content(item) 
    book, datum = None, None
    try :
      book = self[v[0]]
      datum = book[v[1]]
    except KeyError, IndexError : pass
    return (book, datum)

  def setValue(self, v):
    u"""
    sets or adds a new value in Parameter.
    If bloc or parameter does not exists, it is added.
    :param v: a list with 3 values [bloc, parameter, value]
    :type v: list
    """
    (bloc, param) = self.collectionItem(v)
    if not param :
      param = Datum(v[1], unicode)
    if not bloc :
      bloc = Book()
      bloc.name = v[0]
    param.value = Datum.convertValueFrompyLot(v[2], param.nature)
    self._addParameter(bloc.name, param)
    try :
      Collection.setValue(self, bloc.name, param.name, param.value)
    except KeyError:
      print >> sys.stderr, u"Parameters.setValue() : impossible to set %s" % v

  def setParameter(self, block, param, value):
    u"""
    Sets a parameter value, if parameter not exists, create it.
    """
    self.updateValue(block, param, value)

  def getParameter(self, block, param):
    u"""
    Returns parameter value. If not exists, return default value
    """
    return self.getValue(block, param, u"none")
    
  def updateValue(self, block, param, value, nature=unicode):
    u"""
    Updates parameter value, if parameter not exists, create it.
    """
    self._addParameter(block, Datum(param, nature, value))
    self[block][param] = value

  def getValue(self, block, param, default):
    u"""
    Returns parameter value. If not exists, return default value
    """
    try :
      return self[block][param]
    except KeyError :
      self.updateValue(block, param, default)
      return default

  def remove(self, c):
    u"""
    Suppression d'un item  : valeur, parametre ou bloc suivant la valeur de c :

      - si c=[] rien
      - c = [bloc] suppression du I{bloc}
      - c = [bloc, parametre] suppression du parametre I{parametre} du bloc I{bloc}
      - c = [bloc, parametre, valeur] suppression de la valeur I{valeur} du parametre I{parametre} du bloc I{bloc}

    :param c: de la forme [bloc[, parameter[, value]]]
    :type c: list de 0,1 2 ou 3 str
    """
    try :
      if len(c) == 3:
        self[c[0]][c[1]].value_list.remove(c[2])#l'item est une valeur, remove fonctionne sur les listes
        self[c[0]][c[1]].value = self[c[0]][c[1]].value_list[0]
      elif len(c) == 2:self[c[0]].pop(c[1])#pour les dictionnaires, il faut utiliser pop()
      elif len(c) == 1:self.pop(c[0])
    except KeyError, ValueError :
      print >> sys.stderr, u"### Parameters.remove : try to remove a value that is not in parameters"
    except IndexError :
      print >> sys.stderr, u"### Parameters.remove : no available value in value_list for block %s: parameter %s" % (c[0], c[1])

  def _createParametersFile(self, parameters_file, parameters_format):
    u"""
    La Collection est supposée exister.
    """
    self.parameters_file = parameters_file
    self.parameters_format = parameters_format
    self.saveParameters()

if __name__ == u'__main__' :
  from _pylot.core.utils import mkdtempu
  tmpdir = mkdtempu()
  p = Parameters(parameters_file=Path(tmpdir, u'parameters.pylot'),
                 database_file=Path(tmpdir, u'database.csv'),
                 parameters_format=u'pylot',
                 database_format=u'csv')

  p.setValue([u'IoManager', u'inmesh', u'YEAAAAAH!!!!!'])
  p.saveDatabase()

  print p
  print tmpdir
  tmpdir.rmtree()

